/*
 *  Copyright 2008 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.action.core.syndication;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.common.util.DateUtil;
import com.blandware.atleap.common.util.PartialCollection;
import com.blandware.atleap.common.util.QueryInfo;
import com.blandware.atleap.model.core.ActionPage;
import com.blandware.atleap.model.core.ContentResource;
import com.blandware.atleap.model.core.Page;
import com.blandware.atleap.model.core.User;
import com.blandware.atleap.persistence.util.PageAndAnnotation;
import com.blandware.atleap.service.core.ContentResourceManager;
import com.blandware.atleap.service.core.PageManager;
import com.blandware.atleap.service.core.UserManager;
import com.blandware.atleap.webapp.action.core.BaseAction;
import com.blandware.atleap.webapp.util.core.ApplicationResources;
import com.blandware.atleap.webapp.util.core.GlobalProperties;
import com.blandware.atleap.webapp.util.core.WebappConstants;
import com.blandware.atleap.webapp.util.core.WebappUtil;
import com.sun.syndication.feed.synd.*;
import com.sun.syndication.io.SyndFeedOutput;
import org.apache.commons.validator.GenericValidator;
import org.apache.struts.Globals;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * <p>Action that outputs a syndication feed that contains last changes in pages
 * and content resources.</p>
 * <p><a href="LastChangesFeedAction.java.html"><i>View source</i></a></p>
 *
 * @author Roman Puchkovskiy <a href="mailto:roman.puchkovskiy@blandware.com">
 *         &lt;roman.puchkovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.3 $ $Date: 2008/01/14 16:31:39 $
 * @struts.action path="/syndication/lastChanges"
 * name="dummyForm"
 * validate="false"
 */
public final class LastChangesFeedAction extends BaseAction {

    private static final String FEED_TYPE_PARAMETER = "feedType";

    /**
	 * @param mapping  The ActionMapping used to select this instance
	 * @param form     The optional ActionForm bean for this request (if any)
	 * @param request  The HTTP request we are proceeding
	 * @param response The HTTP response we are creating
	 * @return an ActionForward instance describing where and how
	 *         control should be forwarded, or null if response
	 *         has already been completed
	 */
	public ActionForward execute(ActionMapping mapping, ActionForm form,
	                             HttpServletRequest request, HttpServletResponse response) throws Exception {

        String feedType = request.getParameter(FEED_TYPE_PARAMETER);
        // if feed type is not specified, say 'not found'
        if (GenericValidator.isBlankOrNull(feedType)) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return null;
        }

        GlobalProperties globalProperties = GlobalProperties.getInstance(request.getSession().getServletContext());
        ApplicationResources applicationResources = ApplicationResources.getInstance(request.getSession().getServletContext());
        Integer limit = globalProperties.getInteger(WebappConstants.GLOBAL_PROPERTY_SYNDICATION_FEED_SIZE, 10);

		List roles = null;
		String userName = request.getRemoteUser();
		UserManager userManager = (UserManager) getBean(Constants.USER_MANAGER_BEAN);
		if ( userName != null ) {
			User user = userManager.retrieveUser(userName);
			roles = user.getRoles();
		}

		Locale locale = (Locale) request.getSession().getAttribute(Globals.LOCALE_KEY);
        String localeIdentifier = locale.getLanguage();

        Map queryParameters = new HashMap();
        queryParameters.put("localeIdentifier", localeIdentifier);
		queryParameters.put("roles", roles);
        queryParameters.put("skipNewsItems", Boolean.TRUE);
        queryParameters.put("skipActionPages", Boolean.TRUE);

        QueryInfo queryInfo = new QueryInfo();
		queryInfo.setQueryParameters(queryParameters);
		queryInfo.setLimit(limit);

		PageManager pageManager = (PageManager) getBean(Constants.PAGE_MANAGER_BEAN);
        // fetching list of pages without news items and action pages
        List pageList = pageManager.getLastPages(queryInfo);
        
		queryParameters = new HashMap();
		queryParameters.put("localeIdentifier", localeIdentifier);

        queryInfo = new QueryInfo();
        queryInfo.setQueryParameters(queryParameters);
        queryInfo.setWhereClause(" (page.active = 'T') ");
        queryInfo.setOrderByClause("page.lastUpdatedDatetime desc");

        // fetching list of all action pages
        PartialCollection actionPagesCollection = pageManager.listActionPages(queryInfo);
        // truncating list
        List actionPageList = truncateActionPagesCollection(
                actionPagesCollection, limit.intValue(), request);

        // merging lists of pages and action pages
        pageList.addAll(actionPageList);
        Collections.sort(pageList, new Comparator() {
            public int compare(Object o1, Object o2) {
                Page page1 = (Page) o1;
                Page page2 = (Page) o2;
                return -page1.getLastUpdatedDatetime().compareTo(page2.getLastUpdatedDatetime());
            }
        });
        if (pageList.size() > limit.intValue()) {
            pageList = pageList.subList(0, limit.intValue());
        }

        // getting annotations for merged pages list
        List pageAndAnnotationList = pageManager.getPagesAnnotations(pageList, localeIdentifier);

        queryParameters = new HashMap();
		queryParameters.put("roles", roles);

        queryInfo = new QueryInfo();
        queryInfo.setQueryParameters(queryParameters);
		queryInfo.setLimit(limit);

		ContentResourceManager contentResourceManager = (ContentResourceManager) getBean(Constants.CONTENT_RESOURCE_MANAGER_BEAN);
        // fetching list of content resources
        List contentResourceList = contentResourceManager.getLastContentResources(queryInfo);

        SyndFeed feed = new SyndFeedImpl();
        String link = WebappUtil.computeURL(null, null, "/", null, null, request, response, false, false, false, true);
        feed.setLink(WebappUtil.computeExternalURL(link, false, request));
        feed.setDescription("");
        feed.setFeedType(feedType);

        Date titleDate = new Date();
        boolean setTitleDate = false;

        // merging lists of pages and resources
        List entries = new ArrayList();
        int pageIndex = 0;
        int resourceIndex = 0;
        for (int resultIndex = 0; resultIndex < limit.intValue(); resultIndex++) {
            boolean pageIndexFits = (pageIndex < pageAndAnnotationList.size());
            boolean resourceIndexFits = (resourceIndex < contentResourceList.size());
            boolean takePage = false;
            boolean takeResource = false;
            boolean stop = false;
            Page page = null;
            ContentResource contentResource = null;
            if (pageIndexFits && resourceIndexFits) {
                // both fit
                PageAndAnnotation pageAndAnnotation = ((PageAndAnnotation) pageAndAnnotationList.get(pageIndex));
                page = pageAndAnnotation.getPage();
                contentResource = (ContentResource) contentResourceList.get(resourceIndex);
                if (page.getLastUpdatedDatetime().compareTo(contentResource.getLastUpdatedDatetime()) < 0) {
                    // content resource date is after page date
                    takeResource = true;
                } else {
                    takePage = true;
                }
            } else if (pageIndexFits) {
                // only page index fits
                PageAndAnnotation pageAndAnnotation = ((PageAndAnnotation) pageAndAnnotationList.get(pageIndex));
                page = pageAndAnnotation.getPage();
                takePage = true;
            } else if (resourceIndexFits) {
                // only resource index fits
                contentResource = (ContentResource) contentResourceList.get(resourceIndex);
                takeResource = true;
            } else {
                // nothing fits: need to stop
                stop = true;
            }
            if (stop) {
                break;
            } else {
                String title = null;
                String description = null;
                Date date = null;
                if (takePage) {
                    PageAndAnnotation pageAndAnnotation = ((PageAndAnnotation) pageAndAnnotationList.get(pageIndex));
                    title = (String) page.getTitle().get(localeIdentifier);
                    description = pageAndAnnotation.getAnnotation();
                    date = page.getLastUpdatedDatetime();
                    link = WebappUtil.computeURL(page.getUri(), null, null, null, null, request, response, false, false, true, true);
                    pageIndex++;
                } else if (takeResource) {
                    title = WebappUtil.getFieldValue(contentResource.getDescription(), localeIdentifier, request, true);
                    description = null;
                    date = contentResource.getLastUpdatedDatetime();
                    link = WebappUtil.computeURL(null, null, contentResource.getUri(), null, null, request, response, false, false, false, true);
                    resourceIndex++;
                }
                SyndEntry entry = new SyndEntryImpl();
                entry.setTitle(title);
                SyndContent content = new SyndContentImpl();
                content.setType(Constants.MIME_TYPE_HTML);
                content.setValue(description);
                entry.setDescription(content);
                entry.setPublishedDate(date);
                entry.setLink(WebappUtil.computeExternalURL(link, false, request));
                entries.add(entry);
                // date for title
                if (!setTitleDate) {
                    titleDate = date;
                    setTitleDate = true;
                }
            }
        }
        feed.setEntries(entries);

        String formattedDate = DateUtil.formatDate(titleDate, locale);
        feed.setTitle(applicationResources.getMessage(request, "core.syndication.lastChanges.title",
                new Object[]{applicationResources.getMessage(request, "core.commons.webapp.name"), formattedDate}));

        response.setContentType(WebappConstants.FEED_CONTENT_TYPE);
        SyndFeedOutput output = new SyndFeedOutput();
        output.output(feed, response.getWriter());
        return null;
    }

    /**
     * Does following: takes a collection of action pages, retains only those
     * for which current user has rights. Also list is truncated to requested
     * limit.
     *
     * @param actionPagesCollection collection of action pages
     * @param limit                 max size of list to return
     * @param request               request object
     * @return list of action pages
     */
    private List truncateActionPagesCollection(PartialCollection actionPagesCollection,
            int limit, HttpServletRequest request) {
        List actionPages = new ArrayList();
        Iterator i = actionPagesCollection.iterator();
        while (i.hasNext()) {
            ActionPage actionPage = (ActionPage) i.next();
            List roleNames = WebappUtil.getAPRoleNamesAsList(actionPage.getUri(), request);
            if (WebappUtil.isUserInRole(roleNames, false, request)) {
                // action page is allowed, add it to list
                actionPages.add(actionPage);
                if (actionPages.size() >= limit) {
                    // don't need more
                    break;
                }
            }
        }

        return actionPages;
    }
}